﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
using System.Windows.Input;
using Red.Controls.Diagrams;
using Red.Controls.Diagrams.Declarators;
using Red.Controls.Diagrams.ViewModels;
using Red.Controls.Shell;
using Red.Core.Extensions;

namespace Red.Toolkit.NodeDeclaratorEditor
{
    [Export( typeof( IEditorInfo ) )]
    public class EditorInfo : IEditorInfo
    {
        public string Name
        {
            get { return "NodeDeclarator Editor"; }
        }

        public Type WindowClass
        {
            get { return typeof( NodeDeclaratorEditor ); }
        }

        public Type SettingsClass
        {
            get { return typeof( NodeDeclaratorEditorSettings ); }
        }
    }

    /// <summary>
    /// Interaction logic for NodeDeclaratorEditor.xaml
    /// </summary>
    public partial class NodeDeclaratorEditor : RedEditorWindow, INotifyPropertyChanged
    {
        #region Properties

        public NodeDeclaratorEditorViewModel ViewModel => DataContext as NodeDeclaratorEditorViewModel;

        #endregion Properties

        #region C-tor

        public NodeDeclaratorEditor( IEditorHost host, EditorInstanceData instanceData, EditorSharedData sharedData )
            : base( host, instanceData, sharedData )
        {
            DataContext = new NodeDeclaratorEditorViewModel();
            InitializeComponent();
            LoadDeclarators();
            // Loading empty DymmyObjectManager to set IsEditMode to false
            _nodeControl.LoadDummyAsync( null ).DoNotAwait();
        }

        #endregion C-tor

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged( [CallerMemberName] string propertyName = null )
        {
            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
        }

        #endregion INotifyPropertyChanged

        #region Event handlers

        private void NodeControl_SelectionChanged( object sender, SelectionChangedEventArgs e )
        {
            if ( e.AddedItems.Count == 0 )
            {
                ViewModel.SelectedNode = null;
                return;
            }

            // selected one node
            // we are not checking selectedNodesCount == 1 because we can only have one node visible in the editor
            ViewModel.SelectedNode = e.AddedItems[ 0 ] as DummyNodeViewModel;
            _rightDock.ActivePage = _selectionPropertiesPage;

            _outputs.DataContext = ViewModel.Outputs;
            _drops.DataContext = ViewModel.Drops;
        }

        private void Save_CanExecute( object sender, CanExecuteRoutedEventArgs e )
        {
            e.CanExecute = ViewModel.SelectedDeclarator != null;
        }

        private void Save_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            ViewModel.Save( _nodeControl.DeclaratorsPath );
        }

        private async void Declarators_OnSelectionChanged( object sender, SelectionChangedEventArgs e )
        {            
            await _nodeControl.LoadDummyAsync( ViewModel.SelectedDeclarator );
        }

        private void AddDeclarator_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            var newDeclarator = new NodeDeclarator { Class = "New declarator" };
            newDeclarator.VisualType = NodeVisualType.RoundRect;
            ViewModel.AllDeclarators.Add( newDeclarator );
            ViewModel.SelectedDeclarator = newDeclarator;
        }

        private void RemoveDeclarator_CanExecute( object sender, CanExecuteRoutedEventArgs e )
        {
            e.CanExecute = ViewModel.SelectedDeclarator != null;
        }

        private void RemoveDeclarator_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            ViewModel.AllDeclarators.Remove( ViewModel.SelectedDeclarator );
        }

        private void AddOutput_CanExecute( object sender, CanExecuteRoutedEventArgs e )
        {
            // new outputs can be added only on declarators that are not containers
            // containers will generate it's outputs based on output nodes that are inside of the container
            e.CanExecute = ViewModel.SelectedNode != null && !ViewModel.SelectedDeclarator.IsContainer;
        }

        private async void AddOutput_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            // add new output
            var nodeConnectorDeclarator = new NodeOutputDeclarator { Name = "New output" };
            var dummyConnectorViewModel = new DummyConnectorViewModel( nodeConnectorDeclarator, false );
            ViewModel.SelectedNode.OutputConnectors.Add( dummyConnectorViewModel );
            ViewModel.SelectedDeclarator.OutputsInternal.Add(  nodeConnectorDeclarator );

            // refresh outputs list
            await ViewModel.Outputs.RebuildAsync();
        }

        private void AddDrop_CanExecute( object sender, CanExecuteRoutedEventArgs e )
        {
            // new drops can be added only on declarators that are not containers
            e.CanExecute = ViewModel.SelectedNode != null && !ViewModel.SelectedDeclarator.IsContainer;
        }

        private async void AddDrop_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            // add new output
            var nodeDropDeclarator = new NodeDropDeclarator { Class = "New drop" };
            ViewModel.SelectedDeclarator.DropsInternal.Add( nodeDropDeclarator );

            // refresh drops list
            await ViewModel.Drops.RebuildAsync();
        }

        private async void DeleteDummyConnector_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            var dummyConnectorViewModel = e.Parameter as DummyConnectorViewModel;
            dummyConnectorViewModel.ParentNode.OutputConnectors.Remove( dummyConnectorViewModel );

            ViewModel.SelectedDeclarator.OutputsInternal.Remove( dummyConnectorViewModel.Declarator as NodeOutputDeclarator );
            // refresh outputs list
            await ViewModel.Outputs.RebuildAsync();
        }

        private async void DeleteDrop_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            var nodeDeclarator = e.Parameter as NodeDropDeclarator;
            ViewModel.SelectedDeclarator.DropsInternal.Remove( nodeDeclarator );

            // refresh drops list
            await ViewModel.Drops.RebuildAsync();
        }

        private void OrientationHorizontal_CanExecute( object sender, CanExecuteRoutedEventArgs e )
        {
            e.CanExecute = ViewModel.SelectedNode != null;
        }

        private void OrientationHorizontal_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            ViewModel.SelectedNode.DiagramViewModel.Orientation = NodeOrientation.Horizontal;
        }

        private void OrientationVertical_CanExecute( object sender, CanExecuteRoutedEventArgs e )
        {
            e.CanExecute = ViewModel.SelectedNode != null;
        }

        private void OrientationVertical_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            ViewModel.SelectedNode.DiagramViewModel.Orientation = NodeOrientation.Vertical;
        }

        #endregion Event handlers

        #region Private methods

        private void LoadDeclarators()
        {
            var filePath = Path.Combine( SharedData.DepotInfo.RootPath, InstanceData.InstanceContext );
            _nodeControl.DeclaratorsPath = filePath;

            var declarators = _nodeControl.Declarators;
            ViewModel.NodeDeclarators = declarators;
            ViewModel.AllDeclarators = new ObservableCollection<NodeDeclarator>( declarators.GetDeclarators() );
        }

        #endregion Private methods
    }
}
